package com.gitee.easyopen.util;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;

/**
 * redis分布式锁
 * @author tanghc
 */
public class RedisLockUtil {

    private static final Long RELEASE_SUCCESS = 1L;
    // 释放锁脚本
    private static final String SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    // 脚本sha1值
    private static final String SCRIPT_SHA1 = Sha1Util.encrypt(SCRIPT);

    /**
     * 尝试获取分布式锁
     * 
     * @param redisTemplate
     *            Redis客户端
     * @param lockKey
     *            锁
     * @param requestId
     *            请求标识
     * @param expireTimeMilliseconds
     *            超期时间，多少毫秒后这把锁自动释放
     * @return 返回true表示拿到锁
     */
    @SuppressWarnings("unchecked")
    public static boolean tryGetDistributedLock(@SuppressWarnings("rawtypes") final RedisTemplate redisTemplate,
            final String lockKey, final String requestId, int expireTimeMilliseconds) {
        Boolean result = (Boolean) redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                byte[] value = serializer.serialize(requestId);
                return connection.setNX(lockKey.getBytes(), value);
            }
        });

        if (result != null && result) { // 如果加锁成功
            redisTemplate.expire(lockKey, expireTimeMilliseconds, TimeUnit.MILLISECONDS); // 设置超时时间
            return true;
        } else {
            return false;
        }
    }

    /**
     * 释放分布式锁
     * 
     * @param redisTemplate
     *            Redis客户端
     * @param lockKey
     *            锁
     * @param requestId
     *            请求标识
     * @return 返回true表示释放锁成功
     */
    @SuppressWarnings("unchecked")
    public static boolean releaseDistributedLock(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate,
            String lockKey, String requestId) {

        Object result = redisTemplate.execute(new RedisScript<Long>() {
            @Override
            public String getSha1() {
                return SCRIPT_SHA1;
            }

            @Override
            public Class<Long> getResultType() {
                return Long.class;
            }

            @Override
            public String getScriptAsString() {
                return SCRIPT;
            }

        }, Collections.singletonList(lockKey), requestId);

        return RELEASE_SUCCESS.equals(result);
    }

}